import backtrader as bt  
import pandas as pd
#===========策略策略策略策略策略策略策略策略策略策略策略======================================
class MyStrategy(bt.Strategy):
    params = (  
        ('printlog', True),  
        #('stop_loss', 0.03),  # 止损比例，比如3%
        # 大智慧自定义bwwmacd数据
        ("bwwmacd_fast_period", 1),  # 短期（5日）移动平均线周期
        ("bwwmacd_slow_period", 5),  # 长期（30日）移动平均线周期
        #标准macd指标
        ("macd_fast", 12),  # MACD快线周期
        ("macd_slow", 26),  # MACD慢线周期
        ("macd_signal", 9),  # MACD信号线周期
    )
    
    # 初始化策略  
    def __init__(self):  
        
        self.dataclose = self.datas[0].close  
        # 止损比例
        self.stop_loss=0.00
        # 记录买入价格
        self.buy_price = None
        # 纪录交易执行方向, 打印输出
        self.print_list = [] #储存交易信息
        self.ordersta=''# 纪录交易执行方向

        # # ======初始化大智慧自定义bwwmacd短期和长期移动均线指标======
        # self.bwwmacd_fast_sma = bt.indicators.SimpleMovingAverage(self.datas[1].value, period=self.params.bwwmacd_fast_period)
        # self.bwwmacd_slow_sma = bt.indicators.SimpleMovingAverage(self.datas[1].value, period=self.params.bwwmacd_slow_period)
        # self.crossover = bt.ind.CrossOver(self.bwwmacd_fast_sma, self.bwwmacd_slow_sma)  # crossover signal
        #==========xxxxxxx===================================
        self.T=self.datas[1].value
        
        #     # 设置 crossover 不显示
        # self.crossover.plotinfo.plot = False
        
        # ======初始化 MACD 指标======
        self.macd = bt.indicators.MACDHisto(self.datas[0],
                                           period_me1=self.params.macd_fast,
                                           period_me2=self.params.macd_slow,
                                           period_signal=self.params.macd_signal)
                # 检查 MACD 金叉和死叉
        self.macd_line = self.macd.lines.macd  # MACD线（快线-慢线）
        self.signal_line = self.macd.lines.signal  # 信号线
        self.buysize = 0

    def downcast(self, amount, lot): #可买入资金
	    #使用整数除法 amount // lot 来找到 amount 可以被 lot 整除的最大次数。
        return abs(amount // lot * lot) 
        """ 	   
		self 的出现意味着这个函数实际上是类的一个方法，而不是一个独立的函数。
		class MyClass:  
 		   def downcast(self, amount, lot):  
  		      return abs(amount // lot * lot)  

		# 使用方法  
		my_instance = MyClass()  
		result = my_instance.downcast(123, 10)  
		print(result)  # 输出 120
	    """
    # 可以不要，但如果你数据未对齐，需要在这里检验
    # def prenext(self):
    #     print('prenext 执行 ', self.datetime.date(), self.getdatabyname('300015')._name
    #           , self.getdatabyname('300015').close[0])
    #     pass
    
    #=================================NEXT===========================================
    def next(self):
        
        # 计算止损价格
        # macd零上零下区别对待
        if self.macd_line < 0:
            self.stop_loss=0.01
        else:
            self.stop_loss=0.03
        stop_price = self.buy_price * (1 - self.stop_loss) if self.buy_price else 0
        
        # if self.order:
        #     return
        
        # 回测如果是最后一天，则不进行买卖（这段代码放在主程序可以运行，另起策略文件就出错，只能先注释掉）
        # if pd.Timestamp(self.data.datetime.date(0)) == end_date:
        #     return

        if not self.position:  # 没有持仓
            # 执行买入条件判断：收盘价格上涨突破20日均线；
            # 不要在股票剔除日前一天进行买入
            if self.crossover > 0 and self.bwwmacd_fast_sma <-500: # or self.bwwmacd_fast_sma > 0 ):# and self.data1value < self.data1MA:  # 如果信号为1，买入  
                if self.macd_line < 0:
                    # 更新买入价格
                    self.buy_price = self.data.close[0]
                    # print('执行半仓买入') 
                    self.ordersta='半仓买入'
                #     order_value = self.broker.getvalue() * 0.98
                #     order_amount = self.downcast(order_value / self.data.close[0], 100)
                #     #半仓
                #     order_amount=order_amount/2
                #     self.order = self.buy(self.datas[0], order_amount, name=self.datas[0]._name)
                # elif self.macd_line > 0:
                #     self.buy_price = self.data.close[0]
                #     # print('执行全仓买入') 
                #     self.ordersta=('全仓买入') 
                #     order_value = self.broker.getvalue() * 0.98
                #     order_amount = self.downcast(order_value / self.data.close[0], 100)
                #     #全仓
                    
                #     self.order = self.buy(self.datas[0], order_amount, name=self.datas[0]._name)
                #=============================================
                self.buy_price = self.data.close[0]
                # print('执行全仓买入') 
                self.ordersta=('全仓买入') 
                order_value = self.broker.getvalue() * 0.98
                order_amount = self.downcast(order_value / self.data.close[0], 100)
                #全仓
                    
                self.order = self.buy(self.datas[0], order_amount, name=self.datas[0]._name)    
        
        else:

            if self.datas[1].value[0] < self.datas[1].value[-1]: # self.crossover < 0 :

                # 执行卖出
                self.ordersta=('卖出') 
                self.order = self.order_target_percent(self.datas[0], 0, name=self.datas[0]._name)
                
            #     self.ordersta=('止损')   
            #     self.order = self.order_target_percent(self.datas[0], 0, name=self.datas[0]._name)
        pass
    # ==================打印信息============================================
    def notify_order(self, order): # 用于通知和处理订单信息。
        # 检查order.status是否属于两个可能的状态之一：order.Submitted 和 order.Accepted。
        if order.status in [order.Submitted, order.Accepted]:
            # Buy/Sell order submitted/accepted to/by broker - Nothing to do
            return

        # Check if an order has been completed
        # Attention: broker could reject order if not enough cash
        if order.status in [order.Completed, order.Canceled, order.Margin]:
            if order.isbuy():
                """self.log(
                    f"买入{order.info['name']}, 成交量{order.executed.size}，成交价{order.executed.price:.2f} 订单状态：{order.status}")
                self.log('买入后当前资产：%.2f 元' % self.broker.getvalue())"""
                self.log(
                            f"执行:{self.ordersta},"
                            f"代码:{order.info['name']},"
                            f"成交量:{order.executed.size},"
                            f"成交价:{order.executed.price:.2f},"
                            f"当前资产:{self.broker.getvalue():.2f}"
                        )               
            elif order.issell():
                """self.log(
                    f"卖出{order.info['name']}, 成交量{order.executed.size}，成交价{order.executed.price:.2f} 订单状态：{order.status}")
                self.log('卖出后当前资产：%.2f 元' % self.broker.getvalue()) """
                self.log(
                            f"执行:{self.ordersta},"
                            f"代码:{order.info['name']},"
                            f"成交量:{order.executed.size},"
                            f"成交价:{order.executed.price:.2f}," 
                            f"当前资产:{self.broker.getvalue():.2f}"
                        )                
                
            self.bar_executed = len(self)

        # Write down: no pending order
        self.order = None

    def log(self, txt, dt=None): #日志-日期和信息
        """
        输出日期
        :param txt:
        :param dt:
        :return:
        """
        dt = dt or self.datetime.date(0)  # 现在的日期
        #'%s , %s' 是一个字符串模板，其中%s是一个占位符，用于表示一个字符串。
        # 在这个模板中，有两个%s，这意味着我们需要为它们提供两个字符串值。
        # (dt.isoformat(), txt) 是一个元组，它包含了两个值：dt.isoformat() 和 txt。
        # isoformat方法默认会生成一个包含T的ISO 8601格式的字符串（如2023-10-23T14:30:00），
        # 所以在输出的日期和时间之间会有一个T
        print('%s , %s' % (dt.isoformat(), txt))
        self.print_list.append('日期: %s , %s' % (dt.isoformat(), txt))#加入到打印列表,最后一起打印,输出csv
    pass

    def notify_trade(self, trade): #打印交易信息
        '''可选，打印交易信息'''
        pass

    def get_print_list(self):
        return self.print_list